﻿using Engine;
using Engine.Graphics;
using Game;
using GameEntitySystem;
using SAGhoul.ParticleSystem;
using System;
using System.Collections.Generic;

namespace SAGhoul
{
	public static class SAGhoulsManager
	{
		public static SubsystemSAG SubsystemSAG
		{
			get; set;
		}
		public const int MaxPeriodCount = 8;
		public static Dictionary<int, Dictionary<int, string>> Periods
		{
			get; set;
		} = [];
		public static Vector2 Rotation
		{
			get; set;
		}
		public static bool IsBGMPlaying
		{//防止多个玩家Bgm重复播放
			get; set;
		}
		public static int BloodMoonPeriodLengthMultiplier => SubsystemSAG?.PeriodLengthMultiplier ?? 3;
		public static int BloodMoonPeriodLength => 4 * BloodMoonPeriodLengthMultiplier;
		public static int WholeDays => BloodMoonPeriodLength * MaxPeriodCount + 4;

		public const int AnimateTextureFrames = 16;
		static readonly Texture2D[] m_animateTextures = new Texture2D[AnimateTextureFrames];
		public static void Initialize()
		{
			for (int i = 0; i < MaxPeriodCount; i++)
			{
				string contents = ContentManager.Get<string>("Periods/" + (i + 1).ToString());
				if (!string.IsNullOrEmpty(contents))
				{
					int weight = 0;
					Dictionary<int, string> dictionary = [];
					string[] array = contents.Replace("\r", string.Empty).Split(['\n'], StringSplitOptions.RemoveEmptyEntries);
					for (int j = 0; j < array.Length; j++)
					{
						string line = array[j];
						if (line.Contains(';') && !line.StartsWith(';') && !line.EndsWith(';'))
						{
							string[] words = line.Split(';');
							if (words.Length >= 2 && int.TryParse(words[1], out int value))
							{
								if (value <= 0)
									continue;
								string name = words[0];
								weight = MathUtils.Min(100, weight + value);
								dictionary.Add(weight, name);
								if (weight >= 100)
									break;
							}
						}
					}
					Periods.Add(i, dictionary);
				}
			}
			for (int i = 0; i < AnimateTextureFrames; i++)
			{
				m_animateTextures[i] = ContentManager.Get<Texture2D>("Textures/Noise/Noise" + i.ToString());
			}
		}
		public static Texture2D AnimateTextureByTime
		{
			get
			{
				int framesPerSecond = 24;
				var num = Time.FrameStartTime * framesPerSecond;
				int index = (int)(num % AnimateTextureFrames);
				return m_animateTextures[index];
			}
		}
		public static bool IsBloodMoonDay(SubsystemSAG subsystemSAG)
		{
			//return subsystemSAG.SAGEnable && subsystemSAG.m_subsystemSky.MoonPhase == 4;
			if (!subsystemSAG.SAGEnable || subsystemSAG.m_subsystemTartareosity.WorldType != Tartareosity.WorldType.Default)
				return false;
			int moonPhase = subsystemSAG.m_subsystemSky.MoonPhase;
			bool isAbnormalMoon = (moonPhase == 0 || moonPhase == 4);
			if (!isAbnormalMoon)
				return false;
			return MathUtils.Ceiling(subsystemSAG.m_subsystemTimeOfDay.Day - subsystemSAG.m_subsystemTimeOfDay.DayStart) % BloodMoonPeriodLength == 0;
		}

		public static bool IsDuringBloodMoon(SubsystemSAG subsystemSAG)
		{
			return IsBloodMoonDay(subsystemSAG) && subsystemSAG.m_subsystemSky.SkyLightIntensity <= 0.75f;
		}
		public static double GetDaysCount(Project project)
		{
			return GetDaysCount(project.FindSubsystem<SubsystemGameInfo>(throwOnError: true), project.FindSubsystem<SubsystemTimeOfDay>(throwOnError: true));
		}
		//public static double GetDaysCount(SubsystemTimeOfDay subsystemTimeOfDay)
		//{
		//    return subsystemTimeOfDay.Day;
		//}
		public static double GetDaysCount(SubsystemGameInfo subsystemGameInfo, SubsystemTimeOfDay subsystemTimeOfDay)
		{
			return subsystemGameInfo.TotalElapsedGameTime / subsystemTimeOfDay.DayDuration;
		}
		public static int GetPeriod(SubsystemGameInfo subsystemGameInfo, SubsystemTimeOfDay subsystemTimeOfDay)
		{
			return GetPeriod(GetDaysCount(subsystemGameInfo, subsystemTimeOfDay));
		}
		public static int GetPeriod(double daysCount)
		{
			return MathUtils.Clamp((int)MathUtils.Ceiling(daysCount / BloodMoonPeriodLength), 1, MaxPeriodCount);
		}
		public static float GetAngleByVector(Vector2 vector1, Vector2 vector2)
		{//arccos返回非负数,0-pi
			return MathUtils.Acos(Vector2.Dot(vector1, vector2) / vector1.Length() / vector2.Length());
		}
		public static float GetAngleByVector(Vector3 vector1, Vector3 vector2)
		{
			return MathUtils.Acos(Vector3.Dot(vector1, vector2) / vector1.Length() / vector2.Length());
		}
		public static bool IsOnGround(ComponentBody body)
		{
			ComponentLocomotion componentLocomotion = body.Entity.FindComponent<ComponentLocomotion>();
			if (componentLocomotion != null && componentLocomotion.LadderValue.HasValue)
			{
				return true;
			}
			if (body.ParentBody != null)
			{
				return IsOnGround(body.ParentBody);
			}
			if (body.StandingOnBody != null)
			{
				return IsOnGround(body.StandingOnBody);
			}
			return body.StandingOnValue.HasValue || body.StandingOnVelocity != Vector3.Zero || body.ImmersionFactor > 0f;
		}
		public static void SoulsAttack(ComponentCreature target, ComponentCreature attacker)
		{
			if (attacker == null || target == null)
			{
				Log.Warning("Souls Attack must have target and attacker");
				return;
			}
			ComponentSAGPlayer componentSAGPlayer = target.Entity.FindComponent<ComponentSAGPlayer>();
			if (componentSAGPlayer != null)
			{
				componentSAGPlayer.ExplodeSwayT = 1f;
				componentSAGPlayer.ExplodeSwayPower = 80f;
			}

			Vector3 start = attacker.ComponentBody.BoundingBox.Center(), end = target.ComponentBody.BoundingBox.Center();
			var particle = new SoulsAttackParticleSystem(start, start + 2f * Vector3.Normalize(end - start), 2f);
			bool isOnGround = IsOnGround(target.ComponentBody);
			if (isOnGround)
			{//目标滞空则灵魂攻击无效
				particle = new SoulsAttackParticleSystem(start, end, 2f);
				float stunTime;
				if (componentSAGPlayer != null)
				{
					stunTime = 2f;
					float attackNum = 0.33f;
					switch (componentSAGPlayer.m_subsystemSAG.Difficulty)
					{
						case Difficulty.VeryEasy: attackNum = 0.16f; break;
						case Difficulty.Easy: attackNum = 0.25f; break;
						case Difficulty.Hard: attackNum = 0.5f; break;
						case Difficulty.VeryHard: attackNum = 1f; break;
					}
					attackNum += 0.05f;//给一个极小值避免因时间自然衰减导致必须多攻击一次才能满
					componentSAGPlayer.SoulsValue = MathUtils.Clamp(componentSAGPlayer.SoulsValue + attackNum, 0f, 1f);
					componentSAGPlayer.m_componentPlayer.ComponentGui.DisplaySmallMessage(LanguageControl.Get(typeof(SAGhoulsManager).Name, "AttackedBySoul"), Color.White, true, true);
				}
				else
				{
					stunTime = 3f;
					target.ComponentHealth.Injure(0.33f, attacker, false, LanguageControl.Get(typeof(SAGhoulsManager).Name, "SoulImpact"));
				}
				ComponentLocomotion componentLocomotion = target.Entity.FindComponent<ComponentLocomotion>();
				if (componentLocomotion != null)
				{
					componentLocomotion.StunTime = MathUtils.Max(componentLocomotion.StunTime, stunTime);
					componentLocomotion.m_componentCreature.ComponentBody.ApplyImpulse(5f * Vector3.Normalize(Vector3.Normalize(end - start) + Vector3.UnitY));
				}
			}
			target.Project.FindSubsystem<SubsystemParticles>().AddParticleSystem(particle);
		}

		public static int BlockIndex(this Type type)
		{
			return BlocksManager.GetBlockIndex(type);
		}
		public static Block GetBlock(int value)
		{
			return BlocksManager.Blocks[Terrain.ExtractContents(value)];
		}
		public static string GetBlockDisplayName(int value)
		{
			return GetBlock(value).GetDisplayName(null, value);
		}
		public static string GetBlockDisplayName(Type type)
		{
			return GetBlockDisplayName(type.BlockIndex());
		}

		public static void SAGClear(Project project)
		{
			foreach (ComponentCreature componentCreature in project.FindSubsystem<SubsystemCreatureSpawn>(throwOnError: true).Creatures)
			{
				if (componentCreature.Entity.FindComponent<ComponentSAGhouls>() != null)
					project.RemoveEntity(componentCreature.Entity, true);
			}
			foreach (var p in project.FindSubsystem<SubsystemPickables>(throwOnError: true).Pickables)
				p.ToRemove = true;
		}

		public static string Lang(this Component component, string key)
		{
			return LanguageControl.Get(component.GetType().Name, key);
		}
		public static float CalculateSAGArmorProtection(ComponentClothing componentClothing)
		{
			float headSturdinessSum = CalculateArmorSlotSturdiness(componentClothing, ClothingSlot.Head);
			float torsoSturdinessSum = CalculateArmorSlotSturdiness(componentClothing, ClothingSlot.Torso);
			float legsSturdinessSum = CalculateArmorSlotSturdiness(componentClothing, ClothingSlot.Legs);
			float feetSturdinessSum = CalculateArmorSlotSturdiness(componentClothing, ClothingSlot.Feet);
			float head = MathUtils.Lerp(0f, 0.2f, MathUtils.Saturate(headSturdinessSum / 200f));//占比20%，总耐久最大合计200(钻石盔)
			float torso = MathUtils.Lerp(0f, 0.45f, MathUtils.Saturate(torsoSturdinessSum / 275f));//占比45%，总耐久最大合计275(背心+衬衫+皮背心+锁子甲+钻甲)
			float legs = MathUtils.Lerp(0f, 0.2f, MathUtils.Saturate(legsSturdinessSum / 220f));//占比20%，总耐久最大合计220(秋裤+皮裤+钻护腿)
			float feet = MathUtils.Lerp(0f, 0.15f, MathUtils.Saturate(feetSturdinessSum / 200f));//占比15%，总耐久最大合计200(钻石靴)
			return head + torso + legs + feet;
		}
		public static float CalculateArmorSlotSturdiness(ComponentClothing componentClothing, int slot) => CalculateArmorSlotSturdiness(componentClothing, (ClothingSlot)slot);
		public static float CalculateArmorSlotSturdiness(ComponentClothing componentClothing, ClothingSlot slot)
		{
			float result = 0f;
			foreach (var value in componentClothing.GetClothes(slot))
			{
				Block block = BlocksManager.Blocks[Terrain.ExtractContents(value)];
				ClothingData clothingData = block.GetClothingData(value);
				if (clothingData == null)
					continue;
				float maxDurability = block.GetDurability(value) + 1;
				float remainingSturdiness = (maxDurability - block.GetDamage(value)) / maxDurability * clothingData.Sturdiness;
				result += clothingData.ArmorProtection * remainingSturdiness;
			}
			return result;
		}
		public static void SpawnAquagazeLv4(ComponentPlayer componentPlayer)
		{
			Vector3 position = componentPlayer.GameWidget.ActiveCamera.ViewPosition + 3f * componentPlayer.GameWidget.ActiveCamera.ViewDirection;
			Entity entity = DatabaseManager.CreateEntity(componentPlayer.Project, "Aquagaze", true);
			entity.FindComponent<ComponentFrame>(true).Position = position;
			entity.FindComponent<ComponentAquagaze>(true).growthLevel = 4;
			//entity.FindComponent<ComponentSpawn>(true).SpawnDuration = 0f;
			componentPlayer.Project.AddEntity(entity);
		}
	}
}
